/*
 * Copyright Kulikov Victor 2009
 * Distributed under LGPL license

 * Modified by Pavel Shchelkun 2012
 * in his part of a diploma project
 * named DVGEditor <http://code.google.com/p/dataviewer/>
 * and  distributed under GNU GPL license.
 */
#include "polygontool.h"
#include <QGraphicsScene>
#include <QStyle>
#include <QStyleOptionGraphicsItem>
#include <QDebug>
#include <QGraphicsSceneContextMenuEvent>
#include <QTextStream>
#include "editorscene.h"

QSet<QGraphicsItem*> PolygonTool::tools;
PolygonTool::PolygonTool(int number,QGraphicsPolygonItem* poly)
{
    m_poly = poly;
    m_number = number;
    m_transobj=0;
    setFlags(ItemIsMovable | ItemIsSelectable
             | QGraphicsItem::ItemSendsGeometryChanges
             | QGraphicsItem::ItemSendsScenePositionChanges
             | ItemIgnoresTransformations);
    tools.insert(this);
    m_pToolContextMenu = NULL;
}

bool PolygonTool::isTool(QGraphicsItem* item)
{
   if(tools.constEnd()!=tools.find(item))
   {
       return true;
   }
   return false;
}
PolygonTool::~PolygonTool()
{
    tools.remove(this);
}
QRectF  PolygonTool::boundingRect() const
{
    const int Margin = 1;
    return outlineRect().adjusted(-Margin, -Margin, +Margin, +Margin);
}
void PolygonTool::hideAll()
{
    foreach(QGraphicsItem* item,tools)
    {
        item->hide();
    }
}

void PolygonTool::showAll()
{
    foreach(QGraphicsItem* item,tools)
    {
        item->show();
    }
}
QPainterPath  PolygonTool::shape() const
{
    QRectF rect = outlineRect();
    QPainterPath path;
    path.addRect(rect);
    return path;
}
void PolygonTool::paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPen pen(Qt::black);
    pen.setCosmetic(true);
    if (option->state & QStyle::State_Selected) {
        pen.setStyle(Qt::SolidLine);
        pen.setWidth(1);
    }
    painter->setPen(pen);
    painter->setBrush(QBrush(QColor(Qt::red)));
    painter->drawEllipse(outlineRect());
}
QRectF PolygonTool::outlineRect() const
{
    QRectF rect(-5,-5,10,10);
    return rect;
}
QList<PolygonTool*> PolygonTool::createFromPolygon(QGraphicsPolygonItem* poly)
{
    QList<PolygonTool*> list;
    QPolygonF polygon = poly->polygon();
    int i=0;
    foreach(QPointF point, polygon)
    {
        PolygonTool* newtool = new PolygonTool(i,poly);
        newtool->setPos(point);
        list.push_back(newtool);
        newtool->setParentItem(poly);
        i++;
    }
    return list;
}
QList<PolygonTool*> PolygonTool::createFromPolygon(QGraphicsPolygonItem* poly,QGraphicsItem *obj)
{
    if(NULL==obj)
    {
        return createFromPolygon(poly);
    }
    QPolygonF polygon;

    QRectF rect = obj->boundingRect();
    polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft();
    poly->setPolygon(polygon);
    QList<PolygonTool*>  list = createFromPolygon(poly);
    foreach(PolygonTool* tool,list)
    {
        tool->setTransformObject(obj);
    }
    return list;
}

void PolygonTool::setTransformObject(QGraphicsItem* item)
{
    m_transobj = item;
}
bool  PolygonTool::isConvex(QPolygonF poly)
{
    int c = poly.count();
    if(c<3)
        return false;
    for(int i=1;i<c+1;i++)
    {
        QLineF line1(poly[i-1],poly[i%c]);
        QLineF line2(poly[i%c],poly[(i+1)%c]);
        qreal ang = 360-line1.angleTo(line2);
        if(ang>170)
            return false;
    }
    return true;
}
QVariant PolygonTool::itemChange(GraphicsItemChange change,
                        const QVariant &value)
{
    if ((change == QGraphicsItem::ItemScenePositionHasChanged ||
         change == QGraphicsItem::ItemTransformHasChanged ||
         change == QGraphicsItem::ItemPositionHasChanged) && scene())
    {
        QPolygonF poly = m_poly->polygon();
        poly.replace(m_number,this->pos());
        if(this->m_transobj)
        {
            if(isConvex(poly))
            {
                m_poly->setPolygon(poly);
                if(this->m_transobj)
                {
                    QTransform trans;
                    QPolygonF polygon;
                    QRectF rect = m_transobj->boundingRect();
                    polygon << rect.topLeft() << rect.topRight() << rect.bottomRight() << rect.bottomLeft();
                    if(QTransform::quadToQuad(polygon,poly,trans))
                    {
                        m_transobj->setTransform(trans);
                    }
                }
            }
            else
            {
                poly = m_poly->polygon();
                this->setPos(poly.at(m_number));
            }
        }
        else
        {
            m_poly->setPolygon(poly);
        }
    }
    return QGraphicsItem::itemChange(change, value);
}

void PolygonTool::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    //scene()->clearSelection();
    //setSelected(true);
    //qDebug() << m_number;
    //return;
    if (m_pToolContextMenu)
    {
        m_poly->setSelected(true);
        m_poly->setData(NODE_NUMBER, QVariant(m_number));
        m_pToolContextMenu->exec(event->screenPos());
    }
    //QGraphicsItem::contextMenuEvent(event);
}

//MagicPolygon


MagicPolygon::MagicPolygon(int uid, QGraphicsItem *obj)
{
    this->setData(POLYGON_ID, QVariant(QString("polygon%1").arg(uid)));
    this->setData(POLYGON_NAME, QVariant(QString("Object_%1").arg(uid)));

     m_pContextMenu = NULL;
     m_pToolContextMenu = NULL;
     m_layerid=-1;
     m_object=NULL;
     m_pen = QPen(Qt::blue);
     setFlags(QGraphicsItem::ItemIsMovable| QGraphicsItem::ItemIsSelectable
              | QGraphicsItem::ItemSendsGeometryChanges
              | QGraphicsItem::ItemSendsScenePositionChanges);
     m_tools = PolygonTool::createFromPolygon(this,obj);

     if(obj)
     {
        obj->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable
                      | QGraphicsItem::ItemSendsGeometryChanges
                      | QGraphicsItem::ItemSendsScenePositionChanges);
        m_object = obj;
        oldpos = m_object->pos();
     }
     setData(NODE_NUMBER, QVariant(-1));
     m_isEmpty = false;
}
MagicPolygon::~MagicPolygon()
{
    foreach(PolygonTool* tool,m_tools)
    {
        delete tool;
        tool = NULL;
    }
}

void MagicPolygon::setToolContextMenu(QMenu *menu)
{
    m_pToolContextMenu = menu;
    foreach(PolygonTool* tool, m_tools)
    {
        tool->setToolContextMenu(m_pToolContextMenu);
    }
}

void MagicPolygon::hide()
{
   if(m_object)
   {
    m_object->setParentItem(NULL);
    this->m_object->setPos(pos());
   }
   QGraphicsPolygonItem::hide();
}

void MagicPolygon::hideTools()
{
    foreach(PolygonTool* tool,m_tools)
    {
        tool->hide();
    }
}

void MagicPolygon::show()
{
   QGraphicsPolygonItem::show();
   if(m_object)
   {
    this->m_object->setParentItem(this);
    m_object->setPos(this->oldpos);
   }
}
void MagicPolygon::paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPen pen=m_pen;
    pen.setCosmetic(true);
    if (option->state & QStyle::State_Selected)
    {
        pen.setColor(QColor(Qt::blue));
        //pen.setStyle(Qt::DashLine);
        pen.setWidth(2);
    }
    painter->setPen(pen);
    if(!this->m_object)
    {
        painter->setBrush(m_brush);
    }
    painter->drawPolygon(polygon());
    painter->drawText(polygon().boundingRect(), Qt::AlignCenter, data(POLYGON_NAME).toString());
}

 QVariant MagicPolygon::itemChange(GraphicsItemChange change,
                        const QVariant &value)
 {
   //  if(!m_object)
     {
         if(change == ItemSelectedHasChanged && this->isSelected())
         {
            foreach(PolygonTool* tool,m_tools)
            {
                tool->show();
            }
         }
    }
     return QGraphicsItem::itemChange(change, value);
 }

void MagicPolygon::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    scene()->clearSelection();
    setSelected(true);
    if (m_pContextMenu)
    {
        m_pContextMenu->exec(event->screenPos());
    }
}

void MagicPolygon::setLayerId(int id)
{
    this->m_layerid = id;
}

void MagicPolygon::setPolygon(const QPolygonF &polygon)
{
    //cleaning old tools
    foreach(PolygonTool* tool,m_tools)
    {
        delete tool;
        tool = NULL;
    }
    m_tools.clear();
    QGraphicsPolygonItem::setPolygon(polygon);
    //create tools without assosiated object
    m_tools = PolygonTool::createFromPolygon(this);
    setToolContextMenu(m_pToolContextMenu);
    if(this->m_object)
    {
        //if we have an object we need to assosiate our tools with it
        foreach(   PolygonTool* tool,    m_tools)
        {
            tool->setTransformObject(m_object);
        }
        //To assign transformation we must move a tool
        m_tools.first()->moveBy(0.1,0);
        m_tools.first()->moveBy(-0.1,0);
    }
}
void MagicPolygon::setPen(const QPen &pen)
{
    if(this->m_object)
    {
        m_pen=pen;
        QAbstractGraphicsShapeItem* shape = dynamic_cast<QAbstractGraphicsShapeItem*>(m_object);
        if(shape)
        {
            shape->setPen(pen);
        }
    }
    else
    {
        m_pen = pen;
    }
}
void  MagicPolygon::setBinaryMode(bool flag,int layedid)
{
    if(flag)
    {
    //set binary mode
        m_membrush = this->m_brush;
        m_mempen = this->m_pen;
        if(layedid==m_layerid)
        {
            this->setPen(QPen(Qt::white));
            this->setBrush(QBrush(Qt::white));
        }else{
            this->setPen(QPen(Qt::transparent));
            this->setBrush(QBrush(Qt::transparent));
        }
    }
    else
    {
    //restore normal mode
        m_brush = m_membrush;
        m_pen = m_mempen;
        this->setPen(m_pen);
        this->setBrush(m_brush);
    }
}
void MagicPolygon::setBrush(const QBrush &brush)
{
    if(this->m_object)
    {
        m_brush = brush;
        QAbstractGraphicsShapeItem* shape = dynamic_cast<QAbstractGraphicsShapeItem*>(m_object);
        if(shape)
        {

            shape->setBrush(brush);
        }
    }else{
        m_brush = brush;
        this->update();
    }
}
void MagicPolygon::writeToStream(QDataStream* out)
{

    int primid = -1;
    if(m_object)
    {
        primid = m_object->data(0).toInt();
    }
    out->operator <<(primid);
    out->operator <<(this->m_layerid);
    out->operator <<(this->pos().x());
    out->operator <<(this->pos().y());
    *out << m_pen;
    *out << this->m_brush;
  //  out->operator <<(this->m_pen);
    out->operator <<(this->m_tools.count());
    foreach(PolygonTool* tool,this->m_tools)
    {
        out->operator <<(tool->pos().x());
        out->operator <<(tool->pos().y());
    }

}
void MagicPolygon::readFromStream(QDataStream* in)
{
    qreal x,y;
    in->operator >>(this->m_layerid);
    in->operator >>(x);
    in->operator >>(y);
    *in >> this->m_pen;
    *in >> this->m_brush;
    int count;
    *in >>(count);
    QPolygonF pf;
    for(int i=0;i<count;i++)
    {
        qreal x,y;
        *in >>x >> y;
        pf << QPointF(x,y);
    }
    this->setPolygon(pf);
    this->setPos(x,y);
    if(this->m_object)
    {
        m_object->setPos(x,y);
    }
    this->setPen(m_pen);
    this->setBrush(m_brush);
}
//================================================

bool MagicPolygon::saveObject(QTextStream *out)
{
    QString item;
    item  = QString("\t\t<polygon id=\"%1\" name=\"%2\" ")
            .arg(data(POLYGON_ID).toString())
            .arg(data(POLYGON_NAME).toString());
    item += QString("color=\"%1\" zvalue=\"%2\" position=\"%3\" \n")
            .arg(EditorScene::getColorStringRGBA(m_brush.color()))
            .arg(zValue())
            .arg(QString("%1:%2").arg(pos().x()).arg(pos().y()));
    item += "\t\t points=\"";

    foreach(QPointF point, polygon().toList())
    {
        item += QString("%1:%2;")
                .arg(point.x())
                .arg(point.y());
    }
    item += "\"/>\n";
    out->operator <<(item);

    return true;
}

bool MagicPolygon::loadObject(QDomNode node)
{
    QString value;
    value = node.attributes().namedItem("id").nodeValue();
    if (value.isEmpty())
    {
        return false;
    }
    setData(POLYGON_ID,QVariant(value));

    value = node.attributes().namedItem("name").nodeValue();
    if (value.isEmpty())
    {
        return false;
    }
    setData(POLYGON_NAME,QVariant(value));

    value = node.attributes().namedItem("color").nodeValue();
    if (value.isEmpty())
    {
        return false;
    }
    setBrush(QBrush(EditorScene::getColorRGBA(value)));

    value = node.attributes().namedItem("zvalue").nodeValue();
    if (value.isEmpty())
    {
        return false;
    }
    setZValue(value.toDouble());

    value = node.attributes().namedItem("position").nodeValue();
    if (value.isEmpty())
    {
        return false;
    }
    QStringList position = value.split(":");
    if (position.count() != 2)
    {
        return false;
    }
    setPos(position.at(0).toDouble(),position.at(1).toDouble());

    value = node.attributes().namedItem("points").nodeValue();
    if (value.isEmpty())
    {
        return false;
    }
    QStringList points = value.split(";");

    QPolygonF polygon;
    int nodes = 0;
    foreach(QString point, points)
    {
        if (!point.isEmpty())
        {
            QStringList xy = point.split(":");
            if (xy.count() == 2)
            {
                QPointF pf(xy.at(0).toDouble(), xy.at(1).toDouble());
                polygon << pf;
                nodes++;
            }
        }
    }
    if (nodes > 1)
    {
        setPolygon(polygon);
        return true;
    }
    return false;
}

//================================================

void MagicPolygon::RemoveNode()
{
    int number = this->data(NODE_NUMBER).toInt();
    //qDebug()<< number;
    if (number  < 0 ||
        number >= m_tools.count())
    {
        return;
    }

    PolygonTool* tool = m_tools.at(number);
    m_tools.removeAt(number);
    scene()->removeItem(tool);
    delete tool;

    QPolygonF p = polygon();
    p.remove(number);
    QGraphicsPolygonItem::setPolygon(p);

    if (number < m_tools.count())
    {
        for(int i = number; i < m_tools.count(); i++)
        {
            m_tools.at(i)->setNumber(i);
        }
    }
    if (m_tools.count() < 2)
    {
        m_isEmpty = true;
    }
    setData(NODE_NUMBER, QVariant(-1));
}

void MagicPolygon::DuplicateNode()
{
    int number = this->data(NODE_NUMBER).toInt();
    if (number  < 0 ||
        number >= m_tools.count())
    {
        return;
    }

    PolygonTool* tool = m_tools.at(number);
    PolygonTool* newtool = new PolygonTool(number + 1, this);
    QPointF pos;
    pos.setX(tool->pos().x() - 10);
    pos.setY(tool->pos().y());
    newtool->setPos(pos);
    newtool->setParentItem(this);
    newtool->setToolContextMenu(m_pToolContextMenu);
    if(this->m_object)
    {
        newtool->setTransformObject(m_object);
    }
    m_tools.insert(number + 1, newtool);
    QPolygonF p = polygon();
    p.insert(number + 1, newtool->pos());
    QGraphicsPolygonItem::setPolygon(p);

    if ((number + 2) < m_tools.count())
    {
        for(int i = (number + 2); i < m_tools.count(); i++)
        {
            m_tools.at(i)->setNumber(i);
        }
    }
    setData(NODE_NUMBER, QVariant(-1));
}




























































